/*
 * TBlockHashMap.h
 *
 * The hash map for TBlock & its derived classes. All <code>TBlock</code>, <code>VBlock</code> could be found by a 
 * corresponding <code>SPC</code>. 
 * 
 * NOTE : the hash map stores the pointer of <code>TBlock</code> ,<code>VBlock</code>.
 *
 * Created @ 07-07-01
 */

#ifndef TBLOCKHASHMAP_H
#define TBLOCKHASHMAP_H

#include "crossbit/TBlock.h"
#include "crossbit/VBlock.h"
#include "crossbit/common.h"
#include "string.h"
#include <iostream>

namespace crossbit{

#define DEF_BUCKET_SUM  (0x00010000)

    class HashMap{
        public:
            // Bucket
            class Bucket{
                public:
                    XTMemAddr   sc_addr;
                    TBlock*     ptrTBlock;
                    VBlock*     ptrVBlock;
                    //void*     ptrData;
                    Bucket*     next;
                    bool        valid;
                public:
                    Bucket(XTMemAddr addr = -1, TBlock* tData = NULL,
                            VBlock* vData = NULL, Bucket* next = NULL,
                            bool valid = false):
                        sc_addr(addr), ptrTBlock(tData),
                        ptrVBlock(vData), next(next)
                    {}

                    ~Bucket(){}
            }; // Bucket

        public:
            HashMap(MemoryManager *mm, XTUint32 n = DEF_BUCKET_SUM): 
                totalBuckets_(n), bk_id(0)
            {
                MemoryManager	*hm_mm = mm;
                XTUint32	mm_bk_size = n * sizeof(Bucket);
                hashMap_ = new ((Bucket *) hm_mm->allocateObjectPool(&bk_id , mm_bk_size, mm_bk_size)) Bucket[n];

                //hashMap_ = new Bucket[n];
                //memset(hashMap_, 0, n*sizeof(Bucket));
                mask_ = totalBuckets_ - 1;

                XTUint32 tmpmask    = mask_;
                XTInt32 maxbit      = 0;

                XTInt32 i;
                for (i = 0; i < 32 ; ++i){
                    if (mask_ & 0x1 == 0x1)	maxbit = i;
                    mask_ = mask_ >> 1;
                }
                mask_ = 0x1;
                for (i = 0; i < maxbit; ++i)	    mask_ = (mask_ << 1) | 0x1;
                while (mask_ > tmpmask)	    mask_ = mask_ >> 1;
            }

            ~HashMap(){
                Clear();
                // Delete bucket table
                delete [] hashMap_;
            }

        public:
            FINLINE void InsertTBlock(XTMemAddr spc, TBlock* p){
                Bucket* curBucket = Find(spc);
                curBucket -> ptrTBlock = p; 
            }

            FINLINE void InsertVBlock(XTMemAddr spc, VBlock* p){
                Bucket* curBucket = Find(spc);
                curBucket -> ptrVBlock = p;
            }

        private:
            FINLINE Bucket* Find(XTMemAddr spc){
                Bucket* curBucket = hashMap_ + (spc & mask_);

                if (curBucket -> sc_addr == -1){
                    curBucket -> sc_addr = spc;
                    return curBucket;
                }
                else if (curBucket -> sc_addr == spc){
                    return curBucket;
                } 
                else{ 
                    while(curBucket -> next != NULL){
                        curBucket = curBucket -> next;
                        if (curBucket -> sc_addr == spc){
                            return curBucket;
                        } 
                    }
                }

                Bucket* newBucket = new Bucket(spc,NULL,NULL,NULL);

                curBucket -> next = newBucket;

                return newBucket;
            }

        public:
            FINLINE TBlock* FindTBlock(XTMemAddr spc){
                Bucket* curBucket = Find(spc);
                return curBucket -> ptrTBlock;
            }

            VBlock* FindVBlock(XTMemAddr spc){
                Bucket* curBucket = Find(spc);
                return curBucket -> ptrVBlock;
            }

            void Delete(XTMemAddr spc){

                XTInt32 pos = spc & mask_;//Hash(spc);
                Bucket* curBucket = hashMap_ + pos;
                Bucket* nextBucket = NULL;

                if (curBucket -> sc_addr == -1){
                    return; 
                }

                // If found bucket on the table
                if (curBucket -> sc_addr == spc){
                    if (curBucket -> next == NULL){
                        if( curBucket -> ptrVBlock != NULL ){
                            delete curBucket -> ptrVBlock;
                        }
                        if( curBucket -> ptrTBlock != NULL ){
                            delete curBucket -> ptrTBlock;
                        }
                        memset(curBucket, 0, sizeof(Bucket));
                    }
                    else{ 
                        nextBucket = curBucket -> next;
                        curBucket -> sc_addr = nextBucket -> sc_addr;
                        if( curBucket -> ptrVBlock != NULL ){
                            delete curBucket -> ptrVBlock;
                        }
                        if( curBucket -> ptrTBlock != NULL ){
                            delete curBucket -> ptrTBlock;
                        }

                        curBucket -> ptrTBlock = nextBucket -> ptrTBlock;
                        curBucket -> ptrVBlock = nextBucket -> ptrVBlock;
                        curBucket -> next = nextBucket -> next;
                        // Cache is responsible for deleting these objects

                        delete nextBucket;
                    }
                    return ;
                }
                // If not found on the table, search the linklist from
                // this bucket
                else{ 
                    nextBucket = curBucket -> next;

                    while (nextBucket != NULL){ 
                        if (nextBucket -> sc_addr == spc){ 
                            curBucket -> next = nextBucket -> next;
                            //    delete nextBucket -> ptrTBlock -> vblock();

                            delete nextBucket -> ptrTBlock;
                            delete nextBucket -> ptrVBlock;
                            delete nextBucket;

                            return ;
                        }
                        else{
                            curBucket = nextBucket;
                            nextBucket = curBucket->next;
                        }
                    }
                    return ;
                }
            }

        public:
            void Clear(){
                for (XTInt32 i = 0; i < totalBuckets_; i++){

                    Bucket* curBucket = hashMap_ + i;
                    // Delete LinkList from this bucket

                    if (curBucket -> sc_addr != -1){ 
                        delete curBucket -> ptrTBlock;
                        delete curBucket -> ptrVBlock;

                        curBucket = curBucket -> next;
                        Bucket* nextBucket = NULL;

                        while (curBucket != NULL){ 
                            nextBucket = curBucket -> next;
                            //  delete curBucket -> ptrTBlock_ -> vblock();
                            delete curBucket -> ptrTBlock;
                            delete curBucket -> ptrVBlock;
                            delete curBucket;

                            curBucket = nextBucket;
                        }
                    } 
                }
                memset(reinterpret_cast<XTUint8*>(hashMap_), 0, totalBuckets_ * sizeof(Bucket));
            }
            /*
               public:
               void ClearTBlocks(){
               XTInt32 i = 0;
               Bucket* curBucket;
               for (;i < totalBuckets_; i++){
               curBucket = hashMap_ + i;
               if (curBucket -> valid == true){
               delete (TBlock*)(curBucket -> ptrData);
               curBucket -> valid = false;

               if (curBucket -> next == NULL)
               continue;

               Bucket* nextBucket = curBucket -> next_;
               while (nextBucket != NULL){
               curBucket = nextBucket;
               nextBucket = curBucket -> next_;
               delete (curBucket -> ptrTBlock_);
               delete curBucket;
               }
               } 
               }
               }
             */
        public:
            FINLINE XTInt32 TotalBuckets(){
                return totalBuckets_;
            }

            // For debugging
        public:
            void Print(){
                XTInt32 i = 0;
                for (;i < totalBuckets_; i++){
                    Bucket* curBucket = hashMap_ + i;
                    // Delete LinkList from this bucket
                    if (curBucket -> sc_addr == -1){
                        std::cout << "Bucket 0x" << std::hex << curBucket -> sc_addr
                            << "(" << std::hex << (unsigned int)curBucket -> ptrTBlock
                            << ")";
                        curBucket = curBucket -> next;
                        while (curBucket != NULL){
                            std::cout << " --> 0x" << std::hex << curBucket -> sc_addr
                                << "(" << std::hex << (unsigned int)curBucket -> ptrTBlock
                                << ")";
                            curBucket = curBucket -> next;
                        }
                        std::cout << std::endl;
                    } 
                }
            }

        private:
            XTUint32    bk_id;
            Bucket*     hashMap_;
            XTUint32    totalBuckets_;
            XTUint32    mask_;

    }; // HashMap

} // End of namespace crossbit

#endif

